module cache(
input   wire clk_g,
input   wire resetn,
//cpu<->cache
input   wire      valid,
input   wire      op,
input   wire  [7 :0] index,
input  wire[19:0] tag,
input  wire[3 :0] offset,
input  wire[3 :0] wstrb,
input  wire[31:0] wdata,
output  wire   addr_ok,
output  wire   data_ok,
output wire[31:0] rdata,
input   wire is_uncache,
//cache<->axi
output  wire         rd_req,
output    [2 :0] rd_type,
output reg[31:0] rd_addr,
input   wire      rd_rdy,
input   wire      ret_valid,
input  wire ret_last,
input  wire[31:0] ret_data,

output reg       wr_req,
output wire[2 :0] wr_type,
output reg[31:0] wr_addr,
output wire[3 :0] wr_wstrb,
output reg[127:0] wr_data,
input  wire       wr_rdy
);

parameter IDLE=0;
parameter LOOKUP=1;
parameter MISS=2;
parameter REPLACE=3;
parameter REFILL=4;
parameter URead1=5;
parameter URead2=6;
parameter UWrite1=7;
parameter UWrite2=8;

reg [3:0]  status=IDLE;
reg [0:255] D_table1=0;
reg [0:255] D_table2=0;
reg         op_r=0;
reg  [7 :0] index_r=0;
reg  [19:0] tag_r=0;
reg  [3 :0] offset_r=0;
reg  [3 :0] wstrb_r=0;
reg  [31:0] wdata_r=0;
reg  [127:0]inbuf=0;

wire [31:0]data_after_mark;
wire [31:0] rdata11;
wire [31:0] rdata12;
wire [31:0] rdata13;
wire [31:0] rdata14;
wire [31:0] rdata21;
wire [31:0] rdata22;
wire [31:0] rdata23;
wire [31:0] rdata24;

wire [31:0] rdata1=(offset_r[3:2]==2'b00)?(status==REFILL?inbuf[31:0]:rdata11):
                   (offset_r[3:2]==2'b01)?(status==REFILL?inbuf[63:32]:rdata12):
                   (offset_r[3:2]==2'b10)?(status==REFILL?inbuf[95:64]:rdata13):
                   (offset_r[3:2]==2'b11)?(status==REFILL?ret_data:rdata14):0;

wire [31:0] rdata2=(offset_r[3:2]==2'b00)?(status==REFILL?inbuf[31:0]:rdata21):
                   (offset_r[3:2]==2'b01)?(status==REFILL?inbuf[63:32]:rdata22):
                   (offset_r[3:2]==2'b10)?(status==REFILL?inbuf[95:64]:rdata23):
                   (offset_r[3:2]==2'b11)?(status==REFILL?ret_data:rdata24):0;
wire [20:0] TagV_out1;
wire [20:0] TagV_out2;

wire [19:0]tag1=TagV_out1[20:1];
wire [19:0]tag2=TagV_out2[20:1];
wire V1=TagV_out1[0];
wire V2=TagV_out2[0];

wire hit1=(V1&&tag1==tag_r);
wire hit2=(V2&&tag2==tag_r);
wire hit=hit1|hit2;

reg replaceTarget=0;

reg[2:0] refillCount=0;

wire Hit_write=(status==LOOKUP)&op_r&hit;//写命中
wire [31:0]dataIn;

wire [3:0] weaMark=(offset_r[3:2]==2'b00)?4'b0001:
                    (offset_r[3:2]==2'b01)?4'b0010:
                    (offset_r[3:2]==2'b10)?4'b0100:
                    (offset_r[3:2]==2'b11)?4'b1000:0;
wire [3:0] refillMark=(refillCount==3'b000)?4'b0001:
                (refillCount==3'b001)?4'b0010:
                (refillCount==3'b010)?4'b0100:
                (refillCount==3'b011)?4'b1000:0;

wire [3:0]writeWea1={4{hit1}}&weaMark;
wire [3:0]writeWea2={4{hit2}}&weaMark;

wire [3:0]refilWea1={4{~replaceTarget}}&refillMark;
wire [3:0]refilWea2={4{replaceTarget}}&refillMark;

wire [3:0] weain1=(status==REFILL)?refilWea1:
                Hit_write?writeWea1:0;
wire [3:0] weain2=(status==REFILL)?refilWea2:
                Hit_write?writeWea2:0;
wire tvwea1=(status==REFILL&~replaceTarget);
wire tvwea2=(status==REFILL&replaceTarget);
wire [20:0] tvdatain=resetn?{tag_r,ret_last}:0;

wire ena0=addr_ok;
wire enaWrite_toTagV=status==REFILL;
wire enaWrite=Hit_write|(status==REFILL);

wire cur_is_wr=ret_valid&op_r&refillCount==offset_r[3:2];//写不命中时写地址与读入的是否相同
wire [3:0] weain=cur_is_wr|Hit_write?wstrb_r:
            (status==REFILL)?4'b1111:0;

reg [8:0]initcount=0;

wire [7:0]TagVtable_addr_in=~resetn?initcount:
                            (status==REFILL|status==REPLACE)?index_r:index;
wire [7:0]Data_table_addr_in=~resetn?initcount:
                             (status==REFILL)?index_r:index;
TagVtable TagV_table1(.addra(TagVtable_addr_in),.clka(clk_g),.dina(tvdatain),.douta(TagV_out1),.ena(ena0|enaWrite_toTagV),.wea(tvwea1));//�??256�??21
TagVtable TagV_table2(.addra(TagVtable_addr_in),.clka(clk_g),.dina(tvdatain),.douta(TagV_out2),.ena(ena0|enaWrite_toTagV),.wea(tvwea2));

Data_table Data_table11(.addra(Data_table_addr_in),.clka(clk_g),.dina(dataIn),.douta(rdata11),.ena(ena0|enaWrite),.wea({4{weain1[0]}}&weain));//�??256�??32
Data_table Data_table12(.addra(Data_table_addr_in),.clka(clk_g),.dina(dataIn),.douta(rdata12),.ena(ena0|enaWrite),.wea({4{weain1[1]}}&weain));
Data_table Data_table13(.addra(Data_table_addr_in),.clka(clk_g),.dina(dataIn),.douta(rdata13),.ena(ena0|enaWrite),.wea({4{weain1[2]}}&weain));
Data_table Data_table14(.addra(Data_table_addr_in),.clka(clk_g),.dina(dataIn),.douta(rdata14),.ena(ena0|enaWrite),.wea({4{weain1[3]}}&weain));
Data_table Data_table21(.addra(Data_table_addr_in),.clka(clk_g),.dina(dataIn),.douta(rdata21),.ena(ena0|enaWrite),.wea({4{weain2[0]}}&weain));
Data_table Data_table22(.addra(Data_table_addr_in),.clka(clk_g),.dina(dataIn),.douta(rdata22),.ena(ena0|enaWrite),.wea({4{weain2[1]}}&weain));
Data_table Data_table23(.addra(Data_table_addr_in),.clka(clk_g),.dina(dataIn),.douta(rdata23),.ena(ena0|enaWrite),.wea({4{weain2[2]}}&weain));
Data_table Data_table24(.addra(Data_table_addr_in),.clka(clk_g),.dina(dataIn),.douta(rdata24),.ena(ena0|enaWrite),.wea({4{weain2[3]}}&weain));


assign addr_ok=(status==IDLE)|(status==LOOKUP&hit);
assign data_ok=(status==LOOKUP&hit)|(status==REFILL&ret_last)|(status==URead2&ret_last)|(status==UWrite1&wr_rdy);
assign rd_req=status==REPLACE|status==URead1;
assign wr_type=status==UWrite1?3'b010:3'b100;
assign rd_type=status==URead1?3'b010:3'b100;
assign wr_wstrb=(status==UWrite1)?wstrb:4'b1111;
assign rdata=status==URead2?ret_data:
             hit1?rdata1:
             hit2?rdata2:
             ~replaceTarget?rdata1:
             replaceTarget?rdata2:0;

assign dataIn=resetn?(cur_is_wr|Hit_write?wdata_r:ret_data):0;



always @(posedge clk_g) begin
    if(ret_valid)begin
        case(refillCount)
            0:inbuf[31:0]<=ret_data;
            1:inbuf[63:32]<=ret_data;
            2:inbuf[95:64]<=ret_data;
            3:inbuf[127:96]<=ret_data;
        endcase
    end
end

always @(posedge clk_g) begin
    if(~resetn)begin
        status<=0;
        wr_req<=0;
        D_table1<=0;
        D_table2<=0;
        if(initcount<256)initcount<=initcount+1;
    end else initcount<=0;
    case(status)
        IDLE:begin
           if(valid&is_uncache)begin
                if(op)begin
                    wr_data<={96'b0,wdata};//可能需要写回的数据
                    wr_req<=1;
                    wr_addr<={tag,index,offset};
                    status<=UWrite1;
                end
                else begin
                    rd_addr<={tag,index,offset};
                    status<=URead1;
                end
           end
           else if(valid)begin
                status<=LOOKUP;
           end
           else status<=IDLE; 
        end
        LOOKUP:begin
            if(Hit_write)begin
                if(hit1)D_table1[index_r]=1'b1;
                if(hit2)D_table2[index_r]=1'b1;
            end
            if(valid&hit)status<=LOOKUP;
            else if(~hit)status<=MISS;
            else status<=IDLE;
        end
        MISS:begin
            if(wr_rdy)begin
                wr_data<=(replaceTarget)?{rdata24,rdata23,rdata22,rdata21}:{rdata14,rdata13,rdata12,rdata11};//可能需要写回的数据
                if(replaceTarget&D_table2[index_r])begin
                    wr_req<=1;
                    wr_addr<={tag2,index_r,4'b0};
                end
                else if(~replaceTarget&D_table1[index_r])begin
                    wr_req<=1;
                    wr_addr<={tag1,index_r,4'b0};
                end
                status<=REPLACE;
                rd_addr<={tag_r,index_r,4'b0};
            end else status<=MISS;
        end
        REPLACE:begin
            wr_req<=0;
            if(rd_rdy)begin
                status<=REFILL;
                refillCount<=0;
                if(replaceTarget)D_table2[index_r]=1'b0;
                else if(~replaceTarget)D_table1[index_r]=1'b0;
            end
            else status<=REPLACE;
        end
        REFILL:begin
            if(cur_is_wr)begin
                if(replaceTarget)D_table2[index_r]=1'b1;
                else if(~replaceTarget)D_table1[index_r]=1'b1;
            end
            if(ret_valid)begin
                refillCount<=refillCount+1;
            end
            if(ret_valid&&ret_last)begin
                status<=IDLE;
                replaceTarget<=replaceTarget+1;
            end else status<=REFILL;
        end
        URead1:begin//5
            if(rd_rdy)status<=URead2;
        end
        URead2:begin
            if(ret_valid&ret_last)status<=IDLE;
        end
        UWrite1:begin//7
            if(wr_rdy)begin
                wr_req<=0;
                status<=IDLE;
            end
        end
    endcase
    if(addr_ok)begin
        op_r<=op;
        index_r<=index;
        tag_r<=tag;
        offset_r<=offset;
        wstrb_r<=wstrb;
        wdata_r<=wdata;
    end
end


endmodule